package com.xiaodou.mjf.web.xface.core.controller;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.xiaodou.mjf.infrastructure.payeco.Payeco;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.dozer.DozerBeanMapper;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaodou.mjf.application.core.service.InvokeLogService;
import com.xiaodou.mjf.application.core.service.OrderService;
import com.xiaodou.mjf.entity.core.InvokeLog;
import com.xiaodou.mjf.entity.core.Order;
import com.xiaodou.mjf.infrastructure.common.controller.ApiController;
import com.xiaodou.mjf.infrastructure.common.utils.CodeGenerator;
import com.xiaodou.mjf.infrastructure.common.utils.DateUtils;
import com.xiaodou.mjf.infrastructure.common.utils.StringUtils;
import com.xiaodou.mjf.infrastructure.payeco.Constants;
import com.xiaodou.mjf.infrastructure.payeco.client.TransactionH5Client;
import com.xiaodou.mjf.infrastructure.payeco.tools.Tools;
import com.xiaodou.mjf.infrastructure.payeco.tools.Xml;
import com.xiaodou.mjf.infrastructure.payeco.tools.http.HttpClient;
import com.xiaodou.mjf.web.xface.common.API;
import com.xiaodou.mjf.web.xface.common.ResultCode;
import com.xiaodou.mjf.web.xface.core.domain.InvokeLogVO;
import com.xiaodou.mjf.web.xface.core.domain.OrderVO;
import com.xiaodou.mjf.web.xface.core.threads.OpenningAccountThread;

/**
 * Created by Rocky on 3/4/15.
 */
@RestController
@RequestMapping("v1/core")
public class PaymentController extends ApiController {

	@Resource OrderService orderService;
	@Resource InvokeLogService invokeLogService;


    @RequestMapping("invoke")
    public Map<String,Object> payment(String param,HttpServletRequest request,HttpServletResponse response){

        //String param = request.getParameter("param");
        param = param.replace(" ","+");
        String PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANQGnO62bEmWvWw7tetqzOZ1EhoUksCrxw6plt35yI4HK0RLVlKRnBrSMaRJQ/VIbKd6/ZP59NUTJzYhev+8oR6m7Bj7rMj/eKv6nHaedze/CF2F/Dn60pZ2s1mvocdMZiiVirpsp3gTmVvaFXVqWRVuk3DsUzkRJmllrysKEMTLAgMBAAECgYALlCzf0HCLeELx9TTTHWsBLuWFLRhyky70uwg1KR3tu7B+M0sMiU0oHdB+M04VyBW+87WDbkQTms8qYpuv+aImy+m91kqyDO0xJ5guUgq7EwgiIQSsuH3XRMti8Zzz4XvV9OsGG3V+uJDWljY31tGq/pPxHAti/GxiMFhg0JdoqQJBAPIhrb9wDSt9OkzRfI5CCZSZ1Unc0lgFjultKWkRZRjNtgeRCnm9znSMsEnlipwfFrknBBOJxKX0rrFs8i7Tsa8CQQDgK4Aw032yawXCAusGwyfjY6X61NHFqqQ5nFPTRIc41lwKb6LAzMCT2d/GfbyGKUVKIrOpL2Fn4KTV+sD6H3GlAkBAsSrg6Sk7o1u+RoJG5/HzWB6ae3tftSx3xYi438LpG5007FWmLUZJ8YTwWXntOIkzsu7t4XM++97o3LjgNgNVAkBIbCVlIN6qJsNcc77kl2Yqo4X7LIh9GO0no6a9SZV6Ol7QDFMDgOLyoWAXa4XdvjWhhuaOUT1h5wS6EOIJrOD1AkBV2sSqzGqGg/lyKeyZeIDGcSeVHkEQdWrTmKS3aPoEEtYD+xqYQCXquZBS2il6saYTK9rzJJZ7LxGNeyO+I6nk";
        try {

            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(PRIVATE_KEY));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            Key privateKey = keyFactory.generatePrivate(keySpec);

            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE,privateKey);

            byte[] data = Base64.decodeBase64(param);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.length; i += 128) {
                byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i,
                        i + 128));
                sb.append(new String(doFinal));
            }

            String text = sb.toString();

            ObjectMapper objectMapper = new ObjectMapper();

            InvokeLogVO logVO = objectMapper.readValue(text,InvokeLogVO.class);
            OrderVO orderVO = objectMapper.readValue(text,OrderVO.class);

            logVO.setMessage(text);

            if(logVO.getTradeCode().equals("001")) {

                Order order = orderService.get(orderVO.getOrderId(), Order.HAS_PAID);
                if (order != null) {
                    return failure(ResultCode.ORDER_HAS_BEAN_PAID);
                }

                DozerBeanMapper mapper = new DozerBeanMapper();

                InvokeLog log = mapper.map(logVO, InvokeLog.class);
                order = mapper.map(orderVO, Order.class);

                order.setType(Order.TYPE_PAYMENT);
                order.setPaymentStatus(Order.WAIT_PAYMENT);
                order.setTxnDate(DateUtils.getCurrentTime());
                order.setExtOrderId(orderVO.getOrderId());
                if(StringUtils.isNotBlank(order.getMerchantName()))
                    order.setMerchantName(URLDecoder.decode(order.getMerchantName(), "UTF-8"));
                if(StringUtils.isNotBlank(order.getRealName()))
                    order.setRealName(URLDecoder.decode(order.getRealName(), "UTF-8"));
                order.setDescription(URLDecoder.decode(order.getDescription(), "UTF-8"));
                order.setSerialNumber(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()));

                orderService.save(order);

                log.setMerchantName(order.getMerchantName());
                log.setOrderId(order.getId());
                log.setExtOrderId(order.getExtOrderId());
                invokeLogService.save(log);

                String merchantId = Payeco.EC.merchant_id;
                String notifyUrl = API.getNotifyUrl();  //需要做URLEncode
                String returnUrl = API.getReturnUrl();  //需要做URLEncode
                String tradeTime = Tools.getSysTime();
                String expTime = ""; //采用系统默认的订单有效时间
                String notifyFlag = "0";
                String priKey = Payeco.EC.private_key;
                String pubKey = Payeco.EC.public_key;
                String payecoUrl = Payeco.EC.payeco_url;
                //String miscData = "13661634728|||商户测试|440106196911211822|6225887803700003|||";
                String miscData = String.format("%s|||%s|%s|%s|||0|", order.getMobile(),order.getRealName(),order.getCertNo(), orderVO.getCardNumber());

                Xml xml = new Xml();


                String ret = TransactionH5Client.MerchantOrder(merchantId, order.getId(), String.valueOf(order.getAmount()), order.getDescription(), tradeTime, expTime, notifyUrl, returnUrl,
                        "", miscData, notifyFlag, "127.0.0.1", priKey, pubKey, payecoUrl, xml);
                if (!ret.equals("0000")) {
                    String url = API.getHost() + "/v1/core/payment/h5.html?param=";
                    url += param;
                    url += "&reload=true&code="+ret+"&message="+URLEncoder.encode(xml.getRetMsg(),"utf-8");

                    response.sendRedirect(url);
                    return null;
                }

                String redirectUrl = TransactionH5Client.getPayInitRedirectUrl(xml);

                response.sendRedirect(redirectUrl);

                Map<String,Object> result = success();
                result.put("url", redirectUrl);
                return result;
            }else if(logVO.getTradeCode().equals("002")){
                Order order = orderService.get(orderVO.getOrderId(), Order.HAS_PAID);
                if (order == null) {
                	Map<String,Object> result = failure(ResultCode.ORDER_NOT_PAYMENT);
                    result.put("order_id",orderVO.getOrderId());
                    result.put("proc_time",StringUtils.EMPTY);
                    result.put("merchant_id",orderVO.getMerchantId());
                    result.put("amount",order.getAmount());

                    return result;
                }else{
                	Map<String,Object> result = success();
                    result.put("order_id",order.getExtOrderId());
                    result.put("proc_time",DateUtils.formatDate(order.getProcDate()));
                    result.put("merchant_id",order.getMerchantId());
                    result.put("amount",order.getAmount());
                    return result;
                }
            }else if(logVO.getTradeCode().equals("003")){



                DozerBeanMapper mapper = new DozerBeanMapper();

                InvokeLog log = mapper.map(logVO, InvokeLog.class);
                Order order = mapper.map(orderVO, Order.class);

                String orderId = CodeGenerator.UUID();

                order.setId(orderId);
                order.setType(Order.TYPE_DEPOSIT);
                order.setPaymentStatus(Order.WAIT_PAYMENT);
                order.setTxnDate(DateUtils.getCurrentTime());
                order.setExtOrderId(orderId);
                if(StringUtils.isNotBlank(order.getRealName()))
                    order.setRealName(URLDecoder.decode(order.getRealName(), "UTF-8"));
                order.setSerialNumber(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()));

                order.setDescription(URLDecoder.decode(order.getDescription(), "UTF-8"));

                orderService.save(order);

                log.setMerchantName(order.getMerchantName());
                log.setOrderId(order.getId());
                invokeLogService.save(log);

                String merchantId = Payeco.EC.merchant_id;
                String notifyUrl = API.getNotifyUrl();  //需要做URLEncode
                String returnUrl = API.getReturnUrl();  //需要做URLEncode
                String tradeTime = Tools.getSysTime();
                String expTime = ""; //采用系统默认的订单有效时间
                String notifyFlag = "0";
                String priKey = Payeco.EC.private_key;
                String pubKey = Payeco.EC.public_key;
                String payecoUrl = Payeco.EC.payeco_url;
                //String miscData = "13661634728|||商户测试|440106196911211822|6225887803700003|||";
                String miscData = String.format("%s|||%s|%s|%s|||0|", order.getMobile(),order.getRealName(),order.getCertNo(), orderVO.getCardNumber());

                Xml xml = new Xml();


                String ret = TransactionH5Client.MerchantOrder(merchantId, order.getId(), String.valueOf(order.getAmount()), order.getDescription(), tradeTime, expTime, notifyUrl, returnUrl,
                        "", miscData, notifyFlag, "127.0.0.1", priKey, pubKey, payecoUrl, xml);
                if (!ret.equals("0000")) {
                    //return failure(ResultCode.PAYECO_ERROR);
                    //return failure(xml.getRetCode(), xml.getRetMsg());
//                    String url = log.getReturnUrl();
//                    url += "?code=W102&message="+URLEncoder.encode("支付金额超限","UTF-8");
                    String url = API.getHost() + "/v1/core/payment/h5.html?param=";
                    url += param;
                    url += "&reload=true&code="+ret+"&message="+URLEncoder.encode(xml.getRetMsg(),"utf-8");
                    response.sendRedirect(url);
                    return null;
                }

                String redirectUrl = TransactionH5Client.getPayInitRedirectUrl(xml);

                response.sendRedirect(redirectUrl);

                Map<String,Object> result = success();
                result.put("url", redirectUrl);
                return result;

            }


        }catch (Exception e){
            String errCode = e.getMessage();
            e.printStackTrace();
            if ("E101".equalsIgnoreCase(errCode)) {
                return failure(ResultCode.PAYECO_NONE_RETURN);
            } else if ("E102".equalsIgnoreCase(errCode)) {
                return failure(ResultCode.PAYECO_SIGN_INVALID);
            } else if ("E103".equalsIgnoreCase(errCode)) {
                return failure(ResultCode.PAYECO_SIGN_ERROR);
            } else {
                return failure(ResultCode.PAYECO_BROKEN_NETWORK);
            }
        }
        return failure(ResultCode.PAYECO_BROKEN_NETWORK);

    }


    @RequestMapping("remote/notify")
    public String notify(HttpServletRequest request){

        Enumeration<String> paramNames = request.getParameterNames();
        String p = "";
        while (paramNames.hasMoreElements()){
            String paramName = paramNames.nextElement();
            p += paramName + "=" + request.getParameter(paramName) + "&";
        }
        System.out.println(p);

        // 结果通知参数，易联异步通知采用GET提交
        String version = request.getParameter("Version");
        String merchantId = request.getParameter("MerchantId");
        String merchOrderId = request.getParameter("MerchOrderId");
        String amount = request.getParameter("Amount");
        String extData = request.getParameter("ExtData");
        String orderId = request.getParameter("OrderId");
        String status = request.getParameter("Status");
        String payTime = request.getParameter("PayTime");
        String settleDate = request.getParameter("SettleDate");
        String sign = request.getParameter("Sign");

        String respDesc = request.getParameter("RESP_REMARK");
        System.out.println(respDesc);
        try {
            System.out.println(new String(respDesc.getBytes("GBK"),"UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        String retMsgJson = "";

        try {
            boolean b = TransactionH5Client.bCheckNotifySign(version, merchantId, merchOrderId,
                    amount, extData, orderId, status, payTime, settleDate, sign,
                    Constants.PAYECO_RSA_PUBLIC_KEY);
            if (!b) {
                retMsgJson = "{\"RetCode\":\"E101\",\"RetMsg\":\"验证签名失败!\"}";
                System.out.println("verify sign error!");
            }else{
                // 签名验证成功后，需要对订单进行后续处理
                if ("02".equals(status)) { // 订单已支付
                    // 1、检查Amount和商户系统的订单金额是否一致
                    // 2、订单支付成功的业务逻辑处理请在本处增加（订单通知可能存在多次通知的情况，需要做多次通知的兼容处理）；
                    // 3、返回响应内容

                    Order order = orderService.get(merchOrderId);
                    if(order != null && order.getPaymentStatus().equals(Order.WAIT_PAYMENT)){
                        order.setPaymentStatus(Order.HAS_PAID);
                        order.setProcDate(DateUtils.getCurrentTime());
                        orderService.save(order);
                    }

                    retMsgJson = "{\"RetCode\":\"0000\",\"RetMsg\":\"订单已支付\"}";
                    System.out.println("orders has bean paid!");

                    new Thread(){

                        @Override
                        public void run() {
                            try {

                                InvokeLog log = invokeLogService.getByOrderId(order.getId());
                                String notifyUrl = log.getNotifyUrl();
                                String param = "order_id=" + order.getExtOrderId() + "&amount=" + order.getAmount() + "&channel_id="
                                        + order.getChannelId() + "&trade_code="+log.getTradeCode()+"&pay_time=" + DateUtils.formatDate(DateUtils.getCurrentTime(), "yyyyMMddHHmmss") + "&code=0000" + "&message=支付成功";

                                notifyUrl = notifyUrl + "?" + param;
                                System.out.println("第一次发送:" + notifyUrl);

                                String result = new HttpClient().getConnection(notifyUrl, null, "UTF-8", "UTF-8", 5000, 5000, null);
                                int i = 0;
                                while (!result.toUpperCase().equals("SUCCESS") && i < 60) {
                                    i += 10;
                                    Thread.sleep(1000 * i);
                                    result = new HttpClient().getConnection(notifyUrl, null, "UTF-8", "UTF-8", 5000, 5000, null);
                                    System.out.println("第" + i + "次发送:" + notifyUrl);
                                }
                                System.out.println("receited SUCCESS");
                            }catch (Exception e){
                                e.printStackTrace();
                            }
                        }

                    }.start();

                    if(order.getChannelId().startsWith("2")) {
                        new OpenningAccountThread(order.getMobile(), order.getChannelId(), order.getAmount(), order.getId()).start();
                    }


                } else {
                    // 1、订单支付失败的业务逻辑处理请在本处增加（订单通知可能存在多次通知的情况，需要做多次通知的兼容处理，避免成功后又修改为失败）；
                    // 2、返回响应内容
                    retMsgJson = "{\"RetCode\":\"E102\",\"RetMsg\":\"订单支付失败+"+status+"\"}";
                    System.out.println("orders of payment failure!status="+status);

                    Order order = orderService.get(merchOrderId);
//                    if(order != null && order.getPaymentStatus().equals(Order.WAIT_PAYMENT)){
//                        order.setPaymentStatus(Order.HAS_PAID);
//                        order.setProcDate(DateUtils.getCurrentTime());
//                        orderService.save(order);
//                    }

                    new Thread(){

                        @Override
                        public void run() {
                            try {

                                InvokeLog log = invokeLogService.getByOrderId(order.getId());
                                String notifyUrl = log.getNotifyUrl();
                                String param = "order_id=" + order.getExtOrderId() + "&amount=" + order.getAmount() + "&channel_id="
                                        + order.getChannelId() + "&trade_code="+log.getTradeCode()+"&pay_time=" + DateUtils.formatDate(DateUtils.getCurrentTime(), "yyyyMMddHHmmss") + "&code=E102" + "&message="+URLEncoder.encode("支付失败","UTF-8");

                                notifyUrl = notifyUrl + "?" + param;
                                System.out.println("第一次发送:" + notifyUrl);

                                String result = new HttpClient().getConnection(notifyUrl, null, "UTF-8", "UTF-8", 5000, 5000, null);
                                int i = 0;
                                while (!result.toUpperCase().equals("SUCCESS") && i < 60) {
                                    i += 10;
                                    Thread.sleep(1000 * i);
                                    result = new HttpClient().getConnection(notifyUrl, null, "UTF-8", "UTF-8", 5000, 5000, null);
                                    System.out.println("第" + i + "次发送:" + notifyUrl);
                                }
                                System.out.println("received SUCCESS");
                            }catch (Exception e){
                                e.printStackTrace();
                            }
                        }

                    }.start();
                }
            }
        } catch (Exception e) {
            retMsgJson = "{\"RetCode\":\"E103\",\"RetMsg\":\"处理通知结果异常\"}";
            System.out.println("processing error!e=" + e.getMessage());
            e.printStackTrace();
        }

        return retMsgJson;
    }

    @RequestMapping("remote/return")
    public String return_(HttpServletRequest request,HttpServletResponse response) throws IOException {
        // 结果通知参数，易联异步通知采用GET提交
        String version = request.getParameter("Version");
        String merchantId = request.getParameter("MerchantId");
        String merchOrderId = request.getParameter("MerchOrderId");
        String amount = request.getParameter("Amount");
        String extData = request.getParameter("ExtData");
        String orderId = request.getParameter("OrderId");
        String status = request.getParameter("Status");
        String payTime = request.getParameter("PayTime");
        String settleDate = request.getParameter("SettleDate");
        String sign = request.getParameter("Sign");

        Enumeration<String> paramNames = request.getParameterNames();
        String p = "";
        while (paramNames.hasMoreElements()){
            String paramName = paramNames.nextElement();
            p += paramName + "=" + request.getParameter(paramName) + "&";
        }
        System.out.println(p);
        logger.debug(p);


        // 需要对必要输入的参数进行检查，本处省略...

        String retCode = request.getParameter("respCode");
        String baseUrl = API.getHost() + "/v1/order/return_url";
        //String baseUrl = "pay";
        if(retCode !=null && retCode.equals("W101")) {
            String url = baseUrl + "?code=W101&message=" + URLEncoder.encode("用户退出支付", "UTF-8");
            //String url = baseUrl + "?code=W101";
            response.sendRedirect(url);
        }else if(retCode.equals("2004")){
            String url = baseUrl + "?code=W101&message="+ URLEncoder.encode("参数验证失败!","UTF-8");
            //String url = baseUrl + "?code=W101";
            response.sendRedirect(url);
        }else if(retCode.equals("2003") || retCode.equals("2002") || retCode.equals("2005")){
            //String url = baseUrl + "?code=W101";
            String url = baseUrl + "?code=W101&message="+ URLEncoder.encode("支付服务异常","UTF-8");
            response.sendRedirect(url);
        }else{

            // 订单结果逻辑处理
            String retMsgJson = "";
            try {
                //验证订单结果通知的签名
                boolean b = TransactionH5Client.bCheckNotifySign(version, merchantId, merchOrderId,
                        amount, extData, orderId, status, payTime, settleDate, sign,
                        Constants.PAYECO_RSA_PUBLIC_KEY);
                if (!b) {
                    retMsgJson = "{\"RetCode\":\"E101\",\"RetMsg\":\"验证签名失败!\"}";
                    System.out.println("sync verify sign error!");
                }else{
                    // 签名验证成功后，需要对订单进行后续处理
                    Order order = orderService.get(merchOrderId);
                    InvokeLog log = invokeLogService.getByOrderId(order.getId());
                    String returnUrl = log.getReturnUrl();
                    if ("02".equals(status)) { // 订单已支付
                        // 1、检查Amount和商户系统的订单金额是否一致
                        // 2、订单支付成功的业务逻辑处理请在本处增加（订单通知可能存在多次通知的情况，需要做多次通知的兼容处理）；
                        // 3、返回响应内容

                        if(order != null && order.getPaymentStatus().equals(Order.WAIT_PAYMENT)){
                            order.setPaymentStatus(Order.HAS_PAID);
                            order.setProcDate(DateUtils.getCurrentTime());
                            orderService.save(order);
                        }

                        retMsgJson = "{\"RetCode\":\"0000\",\"RetMsg\":\"订单已支付\"}";
                        System.out.println("sync orders has bean paid!");
                        returnUrl += "?code=0000&message="+ URLEncoder.encode("订单已支付","UTF-8");
                    } else {
                        // 1、订单支付失败的业务逻辑处理请在本处增加（订单通知可能存在多次通知的情况，需要做多次通知的兼容处理，避免成功后又修改为失败）；
                        // 2、返回响应内容
                        retMsgJson = "{\"RetCode\":\"E102\",\"RetMsg\":\"订单支付失败+"+status+"\"}";
                        System.out.println("sync orders of payment failure!status="+status);
                        returnUrl += "?code=E102&message="+ URLEncoder.encode("订单支付失败","UTF-8");
                    }
                    response.sendRedirect(returnUrl);

                }
            } catch (Exception e) {
                System.out.println("sync processing error!e=" + e.getMessage());
                String url = baseUrl + "?code=W101&message="+ URLEncoder.encode("支付服务异常","UTF-8");
                //String url = baseUrl + "?code=W101";
                response.sendRedirect(url);
            }
            System.out.println("-----同步通知完成----");
            //返回数据
            return retMsgJson;
        }
        return "";
    }

}
